package css590.dataminer.core.svnminer;

import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.List;
import java.util.ListIterator;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndDocument;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import org.tmatesoft.svn.core.SVNLogEntry;

import css590.dataminer.core.DataFormatType;
import css590.dataminer.core.contracts.IDataDelivery;
import css590.dataminer.core.contracts.IDataFormatter;

public class SvnXmlFormatter implements IDataFormatter{

	private IDataDelivery DeliveryMethod;

	public SvnXmlFormatter(IDataDelivery deliveryMethod){
		//
		// Create filename
		//
		Calendar calendar = Calendar.getInstance();
		SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy_MM_dd_hh_mm_ss");
		
		this.DeliveryMethod = deliveryMethod;
		setXmlFilename("Commit_Record_" + dateFormat.format(calendar.getTime()) + ".xml");
	}

	@Override
	public void transform(List<Object> data) {
		//--do nothing if empty list
		if(data == null || data.isEmpty())
			return;
		//
		// <COMMIT> <USER NAME> <SVN COMMAND> <TIMESTAMP> <USER'S LOG ENTRY> <LIST OF FILES IN CHANGESET>
		//
		System.out.print("Writing " + xmlOutputFile + "...\n");

		try {
			openXmlFile(); // create also level 0 tag

			for (ListIterator<Object> ite = data.listIterator(); ite.hasNext(); )
			{
				SVNLogEntry logEntry = (SVNLogEntry)ite.next();
	
				System.out.print("<COMMIT>");
				System.out.print("<USER NAME>" + logEntry.getAuthor() + "</USER NAME>");
				System.out.print("\n");
	
				createXmlStartTag(COMMIT_TAG_LITERAL, LEVEL_1);
				insertXmlNode("USER_NAME", logEntry.getAuthor(), LEVEL_2);
				insertXmlNode("SVN_COMMAND", "NA", LEVEL_2);
				insertXmlNode("TIMESTAMP", logEntry.getDate().toString(), LEVEL_2);
				insertXmlNode("USER_S_LOG_ENTRY", logEntry.getMessage(), LEVEL_2);
				insertXmlNode("LIST_OF_FILES_IN_CHANGESET", logEntry.getChangedPaths().toString(), LEVEL_2);
				createXmlCloseTag(COMMIT_TAG_LITERAL, LEVEL_1);
			}

			closeXmlFile(); // create also level 0 tag

		} catch (Exception e) {
			e.printStackTrace();
		}

		System.out.print("Done.\n");

		this.getDeliveryMethod().send(null, DataFormatType.XML);
	}

	@Override
	public IDataDelivery getDeliveryMethod() {
		// TODO Auto-generated method stub
		return this.DeliveryMethod;
	}

	private String xmlOutputFile;

	private XMLOutputFactory xmlOutputFactory;
	private XMLEventWriter xmlEventWriter;
	private XMLEventFactory xmlEventFactory;
	private XMLEvent xmlEventNewline;
	private XMLEvent xmlEventTab;

	private final String COMMIT_TAG_LITERAL = "COMMIT";
	private final int LEVEL_0 = 0;
	private final int LEVEL_1 = 1;
	private final int LEVEL_2 = 2;

	public void setXmlFilename(String xmlOutputFile) {
		this.xmlOutputFile = xmlOutputFile;
	}

	private void openXmlFile() throws Exception {
		// assert outputFile

		// create XML event writer
		xmlOutputFactory = XMLOutputFactory.newInstance();
		xmlEventWriter = xmlOutputFactory.createXMLEventWriter(new FileOutputStream(xmlOutputFile, true)); // append

		// create event factory
		xmlEventFactory = XMLEventFactory.newInstance();

		// create newline (XMLEvent)
		// create tab
		xmlEventNewline = xmlEventFactory.createDTD("\n");
		xmlEventTab = xmlEventFactory.createDTD("\t");

		// create document start tag (XMLEvent)
		StartDocument docStart = xmlEventFactory.createStartDocument();
		xmlEventWriter.add(docStart);

		xmlEventWriter.add(xmlEventNewline);

		createXmlStartTag("COMMIT_RECORDS", LEVEL_0);
	}

	private void createXmlStartTag(String name, int level) throws Exception {

		// add tabs
		for (int i = 0; i < level; i++)
		{
			xmlEventWriter.add(xmlEventTab);
		}

		// create open tag
		StartElement startElement = xmlEventFactory.createStartElement("", "", name); // "A StartElement is reported for each Start Tag in the document."
		xmlEventWriter.add(startElement);

		xmlEventWriter.add(xmlEventNewline);
	}

	private void createXmlCloseTag(String name, int level) throws Exception {

		// add tabs
		for (int i = 0; i < level; i++)
		{
			xmlEventWriter.add(xmlEventTab);
		}

		// create open tag
		EndElement endElement = xmlEventFactory.createEndElement("", "", name);
		xmlEventWriter.add(endElement);

		xmlEventWriter.add(xmlEventNewline);
	}

	private void closeXmlFile() throws Exception {

		createXmlCloseTag("COMMIT_RECORDS", LEVEL_0);

		// create document end tag (XMLEvent)
		// "endDocument closes all unclosed start-tags"
		EndDocument docEnd = xmlEventFactory.createEndDocument();
		xmlEventWriter.add(docEnd);
	
		xmlEventWriter.close();
	}

	private void insertXmlNode(String name, String value, int level) throws XMLStreamException {

		// add tabs
		for (int i = 0; i < level; i++)
		{
			xmlEventWriter.add(xmlEventTab);
		}

		// create open tag
		StartElement startElement = xmlEventFactory.createStartElement("", "", name);
		xmlEventWriter.add(startElement);

		// create content (XMLEvent)
		Characters characters = xmlEventFactory.createCharacters(value);
		xmlEventWriter.add(characters);

		// create end tag
		EndElement endElement = xmlEventFactory.createEndElement("", "", name);
		xmlEventWriter.add(endElement);

		xmlEventWriter.add(xmlEventNewline);
	}
}















